#include "vc1dec_api.h"
#include "vc1dec_definitions.h"
#include "vc1stream.h"

#include <string.h>

typedef enum { MODE_RAW = 0, MODE_NORM2, MODE_DIFF2, MODE_NORM6, MODE_DIFF6, MODE_ROWSKIP, MODE_COLSKIP, NOT_DECIDED} BITPLANE_MODE;

const short vc1_norm6_vlc[556][2] = 
{
{3,8},
{3,8},
{5,8},
{5,8},
{6,8},
{6,8},
{9,8},
{9,8},
{10,8},
{10,8},
{12,8},
{12,8},
{17,8},
{17,8},
{18,8},
{18,8},
{20,8},
{20,8},
{24,8},
{24,8},
{33,8},
{33,8},
{34,8},
{34,8},
{36,8},
{36,8},
{40,8},
{40,8},
{48,8},
{48,8},
{-1,0},
{-1,0},
{-1,0},
{512,-1},
{514,-1},
{516,-1},
{518,-1},
{520,-1},
{522,-1},
{524,-1},
{526,-1},
{528,-1},
{530,-1},
{532,-1},
{534,-1},
{536,-1},
{538,-1},
{-1,0},
{540,-4},
{-1,0},
{62,9},
{61,9},
{59,9},
{55,9},
{47,9},
{31,9},
{63,6},
{63,6},
{63,6},
{63,6},
{63,6},
{63,6},
{63,6},
{63,6},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{1,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{2,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{4,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{8,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{16,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{32,4},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{0,1},
{-1,0},
{35,1},
{-1,0},
{37,1},
{38,1},
{7,1},
{-1,0},
{41,1},
{42,1},
{11,1},
{44,1},
{13,1},
{14,1},
{-1,0},
{-1,0},
{49,1},
{50,1},
{19,1},
{52,1},
{21,1},
{22,1},
{-1,0},
{56,1},
{25,1},
{26,1},
{-1,0},
{28,1},
{-1,0},
{60,4},
{58,4},
{57,4},
{54,4},
{53,4},
{51,4},
{46,4},
{45,4},
{43,4},
{39,4},
{30,4},
{29,4},
{27,4},
{23,4},
{15,4},
{-1,0},
};

static VOID_VC1 VC1DecodeNormDiff2(sVC1Decoder* pDec,U8_VC1* pBitplane)
{
	I32_VC1 i = 0, value = 0;
	I32_VC1 numMBs = pDec->widthMBs * pDec->heightMBs;
	sVC1Stream* pStream = &pDec->stream;
	U8_VC1* bitplane = pBitplane;
	if(numMBs & 1)
		*bitplane++ = VC1GetBits(pStream,1);

	for(i = numMBs & 1; i < numMBs; i+=2)
	{
		value = VC1GetBits(pStream,1);
		if(value)
		{
			value = VC1GetBits(pStream,2);
			if(value == 0)
			{
				*bitplane++ = 1;
				*bitplane++ = 0;
			}
			else if(value == 1)
			{
				*bitplane++ = 0;
				*bitplane++ = 1;
			}
			else
			{
				*bitplane++ = 1;
				*bitplane++ = 1;
			}
		}
		else
		{
			*bitplane++ = 0;
			*bitplane++ = 0;
		}
	}

}

static U32_VC1 Get_VLC(sVC1Decoder* pDec,const I16_VC1(*table)[2],I32_VC1 bits,I32_VC1 max_depth)
{
	I32_VC1 n, nb_bits;
    U32_VC1 index;
	I32_VC1 code;
	sVC1Stream* pStream = &pDec->stream;

    index= VC1ShowBits(pStream,bits);
    code = table[index][0];
    n    = table[index][1];

    if(max_depth > 1 && n < 0){
        VC1DumpBits(pStream,bits);

        nb_bits = -n;

        index= VC1ShowBits(pStream,nb_bits) + code;
        code = table[index][0];
        n    = table[index][1];
        if(max_depth > 2 && n < 0){
            VC1DumpBits(pStream,nb_bits);
            nb_bits = -n;

            index= VC1ShowBits(pStream,nb_bits) + code;
            code = table[index][0];
            n    = table[index][1];
        }
    }
    VC1DumpBits(pStream,n);
	return code;
}

static VOID_VC1 decode_rowskip(sVC1Decoder* pDec,U8_VC1* plane, I32_VC1 width, I32_VC1 height, I32_VC1 stride)
{
	int x, y;
	sVC1Stream* pStream = &pDec->stream;
    for (y=0; y<height; y++){
        if (!VC1GetBits(pStream,1)) //rowskip
            memset(plane, 0, width);
        else
            for (x=0; x<width; x++)
			{
                plane[x] = VC1GetBits(pStream,1);
			}
        plane += stride;
    }
}

static VOID_VC1 decode_colskip(sVC1Decoder* pDec,U8_VC1* plane, I32_VC1 width, I32_VC1 height, I32_VC1 stride)
{
	int x, y;
	sVC1Stream* pStream = &pDec->stream;
    for (x=0; x<width; x++){
        if (!VC1GetBits(pStream,1)) //colskip
            for (y=0; y<height; y++)
                plane[y*stride] = 0;
        else
            for (y=0; y<height; y++)
			{
                plane[y*stride] = VC1GetBits(pStream,1);
			}
        plane ++;
    }
} 

static VOID_VC1 VC1DecodeNormDiff6(sVC1Decoder* pDec,U8_VC1* pBitplane)
{
	I32_VC1 x = 0, y = 0,value = 0;
	I32_VC1 numMBs = pDec->widthMBs * pDec->heightMBs;
	sVC1Stream* pStream = &pDec->stream;
	U8_VC1* bitplane = pBitplane;
	I32_VC1 startX = 0, startY = 0;
	I32_VC1 xMBs = pDec->widthMBs, yMBs = pDec->heightMBs;

	if(((yMBs % 3) == 0) && (xMBs % 3))
	{
		///2x3 tile
		U8_VC1* p = bitplane;
		startX = xMBs & 1;
		for(y = 0; y < yMBs; y+=3)
		{
			for(x = startX; x < xMBs; x+=2)
			{
				U32_VC1 value = Get_VLC(pDec,vc1_norm6_vlc,9,1);
				*(p) = value & 1;
				*(p+1) = (value >> 1)&1;
				*(p + xMBs) = (value >> 2)&1;
				*(p + xMBs + 1) = (value >> 3)&1;
				*(p + xMBs + xMBs) = (value >> 4)&1;
				*(p + xMBs + xMBs + 1) = (value >> 5)&1;
			}

			p += ((xMBs<<1) + xMBs);
		}

		if(xMBs & 1)
			decode_colskip(pDec,bitplane,1,yMBs,xMBs);
	}
	else
	{
		///3x2 tile
		U8_VC1* p = bitplane + (yMBs &1)*xMBs;
		for(y = yMBs&1; y < yMBs; y+=2)
		{
			for(x = (xMBs % 3); x < xMBs; x+=3)
			{
				U32_VC1 value = Get_VLC(pDec,vc1_norm6_vlc,9,1);
				*(p) = value & 1;
				*(p+1) = (value >> 1)&1;
				*(p + 2) = (value >> 2)&1;
				*(p + xMBs) = (value >> 3)&1;
				*(p + xMBs + 1) = (value >> 4)&1;
				*(p + xMBs + 2) = (value >> 5)&1;
			}

			p += (xMBs<<1);
		}

		x = xMBs % 3;
		if(x)
			decode_colskip(pDec,bitplane,x,yMBs,xMBs);

		if(yMBs & 1)
			decode_colskip(pDec,bitplane + x,xMBs-x,1,xMBs);
	}
}

HRESULT_VC1 VC1DecodeBitplane(sVC1Decoder* pDec, I8_VC1 index)
{
	HRESULT_VC1 rt = VC1_OK;
	I32_VC1 offset = (((pDec->widthMBs * pDec->heightMBs + 3) >> 2) << 2) * index;
	U8_VC1* bitplane = pDec->bitplane + offset;
	sVC1Stream* pStream = &pDec->stream;
	BITPLANE_MODE mode = NOT_DECIDED;
	I8_VC1 value = 0,invert = 0;
	I32_VC1 width,height;
	width = pDec->widthMBs;
	height = pDec->heightMBs;

	invert = VC1GetBits(pStream,1);

	value = VC1GetBits(pStream,2);
	if(value == 2)
		mode = MODE_NORM2;
	else if(value == 3)
		mode = MODE_NORM6;
	else if(value == 1)
	{
		value = VC1GetBits(pStream,1);
		if(value)
			mode = MODE_COLSKIP;
		else
			mode = MODE_ROWSKIP;
	}
	else
	{
		value = VC1GetBits(pStream,2);
		if(value == 1)
			mode = MODE_DIFF6;
		else if(value == 0)
			mode = MODE_RAW;
		else
			mode = MODE_DIFF2;
	}

	switch(mode)
	{
	case MODE_RAW:
		pDec->bitplanerawmode |= (1 << index);
		return rt;
	case MODE_NORM2:
	case MODE_DIFF2:
		VC1DecodeNormDiff2(pDec,bitplane);
		break;
	case MODE_NORM6:
	case MODE_DIFF6:
		VC1DecodeNormDiff6(pDec,bitplane);
		break;
	case MODE_ROWSKIP:
		decode_rowskip(pDec,bitplane,width,height,width);
		break;
	case MODE_COLSKIP:
		decode_colskip(pDec,bitplane,width,height,width);
		break;
	default:
		return VC1_UNKNOWERROR;
	}

	/* Applying diff operator */
    if (mode == MODE_DIFF2 || mode == MODE_DIFF6)
    {
		int x,y;
        U8_VC1* planep = bitplane;
		I32_VC1 stride = width;
        planep[0] ^= invert;
        for (x=1; x<width; x++)
            planep[x] ^= planep[x-1];
        for (y=1; y<height; y++)
        {
            planep += stride;
            planep[0] ^= planep[-stride];
            for (x=1; x<width; x++)
            {
                if (planep[x-1] != planep[x-stride]) planep[x] ^= invert;
                else                                 planep[x] ^= planep[x-1];
            }
        }
    }
    else if (invert)
    {
		int x;
        U8_VC1* planep = bitplane;
        for (x=0; x<width*height; x++) planep[x] = !planep[x]; //FIXME stride
    }

	return rt;
}